package dbu.dbase.version3;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;

import dbu.dbase.DBaseStructure;

/**
 * Multiple Index file header.
 * 
 * The index file is a B+ tree - at least according to most description -
 * but it's more clearly described as a paged B-tree.
 * The essential structure is in an inverted tree, with an anchor note,
 * multiple root nodes, and leaf nodes. The header is called the anchor
 * node.
 *
 * Search algorithm for index files
 *
 *  1. The anchor node always resides in memory, while the file is open
 *     and determines which root node to access for at given key.
 *  2. The root node is read and sequentially scanned until a key is
 *     found that is >= to the desired key.
 *  3. A second-level node is accessed and scanned in a manner similar to
 *     the root node.
 *  4. The process continues until the pointer to the next-lower-level
 *     node has a value of zero. If the key matches the leaf key at this
 *     level, the record number for the key is returned.
 *     That's a discipline NOT to be discussed here :-) Way out of the
 *     scope fore this document and way too complex.
 *
 *  One of the advances of the B+ tree is that the higher levels of the
 *  tree does not have to be updated when a certain node is changed -
 *  unless the tree has become unbalanced.
 *
 * @author orlin
 *
 */
public class MdxFileHeder extends DBaseStructure {

	/** Version number */
	public byte version;

	/**
	 * Date of creation; in YYMMDD format.  
	 * Each byte contains the number as a binary.  YY is added to a base of 1900 decimal to determine the actual year. 
	 * Therefore, YY has possible values from 0x00-0xFF, which allows for a range from 1900-2155.
	 */
	private Date creation;

	/** Data file name (no extension) */
	private String name;

	/** Block size */
	private short blockSize;

	/** Block size adder N */
	private short blockAdder;

	/** Production index flag */
	private byte productionIndex;

	/** Number of entries in tag table. Max no. is 48 (30h) */
	private byte entries;

	/** Length of each tag table entry. Max is 32 (20h) */
	private byte tagLength;

	/** No.of tags in use */
	private short tagUsed;

	/** No.of pages in tagfile */
	private int pages;

	/** Pointer to first free page */
	private int freePage;

	/** No.of block available */
	private int availableBlocks;

	/** Date of last update; in YYMMDD format. */
	private Date lastUpdate;

	/** Tag Table */
	private Collection<MdxTagEntry> tagTableEntries;

	public MdxFileHeder() {
		tagTableEntries = new ArrayList<MdxTagEntry>();
	}

	/**
	 * Read  
	 * @param stream
	 * @throws IOException
	 */
	@Override
	protected int read(InputStream stream) throws IOException {
		/** 1 byte */
		version = readByte(stream);
		/** 3 bytes */
		creation = readYYMMDD(stream);
		/** 16 bytes */
		name = readString(stream, 16);
		/** 2 byte */
		blockSize = readShort(stream);
		/** 2 byte */
		blockAdder = readShort(stream);
		/** 1 byte */
		productionIndex = readByte(stream);
		/** 1 byte */
		entries = readByte(stream);
		/** 1 byte */
		tagLength = readByte(stream);
		/** 1 byte 	Reserved */
		readByte(stream);
		/** 2 byte */
		tagUsed = readShort(stream);
		/** 2 byte 	Reserved */
		readShort(stream);
		/** 4 byte */
		pages = readInt(stream);
		/** 4 byte */
		freePage = readInt(stream);
		/** 4 byte */
		availableBlocks = readInt(stream);
		/** 3 bytes */
		lastUpdate = readYYMMDD(stream);
		/** 1 byte 	Reserved */
		readByte(stream);

		/** 496 bytes 	Garbage */
		stream.skip(496);
		/**/System.out.println(this);

		/** Tag Table Entries */
		for (int i = 0; i < tagUsed; i++) {
			MdxTagEntry tagEntry = new MdxTagEntry();
			tagEntry.read(stream);
			/**/System.out.println(tagEntry);
			tagTableEntries.add(tagEntry);
		}

		/** Garbage */
		long skip = stream.skip(blockSize * blockAdder - 48 - 496 - tagUsed * tagLength); /** 544 */
		//System.out.println("skip: " + skip);

		/** Tag Header */
		MdxTagHeader tag = new MdxTagHeader();
		int size = tag.read(stream);
		/**/System.out.println(tag);

		skip = stream.skip(512L * tag.getRoot() - blockSize * blockAdder - size);
		//System.out.println("skip: " + skip);

		MdxPage page = new MdxPage();
		size = page.read(stream);
		
		return -1;
	}

	/**
	 * Write 
	 * @param stream
	 * @throws IOException
	 */
	protected void write(OutputStream stream) throws IOException {
		/** 1 byte */
		stream.write(version);
		/** 3 bytes */
		stream.write(getYYMMDD(creation));
		/** 16 bytes */
		writeString(stream, name, 16);
		/** 2 byte */
		stream.write(blockSize);
		/** 2 byte */
		stream.write(blockAdder);
		/** 1 byte */
		stream.write(productionIndex);
		/** 1 byte */
		stream.write(entries);
		/** 1 byte */
		stream.write(tagLength);
		/** 1 byte	Reserved */
		stream.write(new byte[] {0});
		/** 2 byte */
		stream.write(tagUsed);
		/** 2 byte	Reserved */
		stream.write(new byte[] {0, 0});
		/** 4 byte */
		stream.write(pages);
		/** 4 byte */
		stream.write(freePage);
		/** 4 byte */
		stream.write(availableBlocks);
		/** 3 bytes */
		stream.write(getYYMMDD(lastUpdate));
		/** 1 byte	Reserved */
		stream.write(new byte[] {0});

		/** 496 bytes 	Garbage*/
		byte[] garbage = new byte[496];
		stream.write(garbage);

		/** Tag Table Entries */
		for (MdxTagEntry tagEntry: tagTableEntries) {
			tagEntry.write(stream);
		}

		/** Garbage */
		stream.write(new byte[blockSize * blockAdder - 48 - 496 - tagUsed * tagLength]);
	}

	@Override
	public String toString() {
		StringBuffer result = new StringBuffer();
//		result.append("version:    "); result.append(version); result.append("\n");
//		result.append("creation:   "); result.append(creation); result.append("\n");
//		result.append("name:       "); result.append(name); result.append("\n");
		result.append("blockSize:  "); result.append(blockSize); result.append("\n");
		result.append("blockAdder: "); result.append(blockAdder); result.append("\n");
//		result.append("prodIndex:  "); result.append(productionIndex); result.append("\n");
		result.append("entries:    "); result.append(entries); result.append("\n");
//		result.append("tagLength:  "); result.append(tagLength); result.append("\n");
//		result.append("tagUsed:    "); result.append(tagUsed); result.append("\n");
		result.append("pages:      "); result.append(pages); result.append("\n");
//		result.append("freePage:   "); result.append(freePage); result.append("\n");
//		result.append("available:  "); result.append(availableBlocks); result.append("\n");
//		result.append("lastUpdate: "); result.append(lastUpdate); result.append("\n");
		return result.toString();
	}

	public static void main(String[] args) {
		File file = new File("/users/otomov/projects/dbu/db", "groups.mdx");
		try {
			InputStream in = new FileInputStream(file);
			MdxFileHeder mdx = new MdxFileHeder();
			mdx.read(in);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}
